<?php

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 * Description of BAuthManager
 *
 * @author jianfeng
 */
class BAuthManager {
    //put your code here 

    const WORKTEAMADMINROLE = "workteam_admin";
    const SERVEDTEAMADMINROLE = 'servedteam_admin';
    const WORKTEAMMEMBERROLE = 'workteam_member';
    const SERVEDTEAMMEMBERROLE = 'servedteam_member';
    const SERVICEADMINROLE = 'service_admin';
    const SERVICEMEMBERROLE = 'service_member';
    const MIXEDTYPEOFROLE = "service_mixed";
    const NONEDEFINEDROLE = "service_none";

    private $app = null;
    private $appId = null;
    private $userRolesList = array();

    public function __construct($serviceApp = NULL) {

        if ($serviceApp != NULL) {
            if (is_numeric($serviceApp)) {
                $this->appId = $serviceApp;
            } else {
                $this->app = $serviceApp;
                $this->appId = $this->app->getId();
            }
        }
    }

    // public function checkAccess($operation, $userId);   

    public function addChildRole($parent, $child) {

        if (is_string($parent))
            $parentId = $this->getRoleId($parent);

        if (is_string($child))
            $childId = $this->getRoleId($child);

        if ($parent instanceof BAuthItem)
            $parentId = $parent->getId();

        if ($child instanceof BAuthItem)
            $childId = $child->getId();

        $db = Blyn::app()->getAppDb();

        $db->addChildRole($parentId, $childId);
    }

    public function addOperationToRole($role, $operations) {

        $db = Blyn::app()->getAppDb();

        foreach ($operations as $operation) {

            $db->addOperationtoRole($operation->getId(), $role->getId());
        }
    }

    public function assignUserToRole($service, $user, $role) {

        if (is_numeric($service))
            $serviceId = $service;
        if (is_numeric($user))
            $userId = $user;
        if (is_numeric($role))
            $roleId = $role;

        if ($service instanceof BService)
            $serviceId = $service->getId();
        if ($user instanceof BUser)
            $userId = $user->getId();
        if ($role instanceof BAuthItem)
            $roleId = $role->getId();

        $db = Blyn::app()->getAppDb();

        return $db->assignUserRole($serviceId, $userId, $roleId);
    }

    public function assignCircleToRole($service, $circle, $role) {
        $db = Blyn::app()->getAppDb();

        $db->assignCircleRole($service->getId(), $user->getId(), $role->getId());
    }

    public function getRoles($serviceApp) {

        $db = Blyn::app()->getAppDb();

        $roles = $db->getServiceAppRoles($appId);

        return $roles;
    }

    public function getRoleId($name, $sAppId = NULL) {

        $bBasicTemplate = new BBasicRolesTemplate();

        $basicRoles = $bBasicTemplate->getAllBasicRoles();

        if (in_array($name, $basicRoles)) {
            $appId = Blyn::app()->getAppDb()->getServiceAppId("BServiceEngine");           
        } else {
            if ($sAppId != NULL) {
                $appId = $sAppId;
            } else {
                $appId = $this->appId;
            }
        }

        //$db = Blyn::app()->getAppDb();        

        $mRole = BMApplicationAuthItem::model()->findBySql("select * from bln_application_authitem 
            where authname = :AuthName and app_id = :AppId", array(":AuthName" => $name, ':AppId' => $appId));

        if ($mRole == NULL)
            return FALSE;

        //$role = new BAuthItem($mRole->_id);

        return $mRole->_id;
    }

    public function getRole($name, $sAppId = NULL) {

        $bBasicTemplate = new BBasicRolesTemplate();

        $basicRoles = $bBasicTemplate->getAllBasicRoles();

        if (in_array($name, $basicRoles)) {
            $serviceEngine = new BServiceEngine();
            $appId = $serviceEngine->getId();
        } else {
            if ($sAppId != NULL) {
                $appId = $sAppId;
            } else {
                $appId = $this->appId;
            }
        }

        $mRole = BMApplicationAuthItem::model()->findBySql("select * from bln_application_authitem 
            where authname = :AuthName and app_id = :AppId", array(":AuthName" => $name, ':AppId' => $appId));

        if ($mRole == NULL)
            return FALSE;

        $role = new BAuthItem($mRole->_id);

        return $role;
    }

    public function createRole($name, $type, $showname) {

        $db = Blyn::app()->getAppDb();

        $roleId = $db->createRole($this->appId, $name, $type, $showname);

        return new BAuthItem($roleId);
    }

    public function addRole($name, $type, $showname) {
        $db = Blyn::app()->getAppDb();

        $roleId = $db->addRole($this->appId, $name, $type, $showname);

        return $roleId;
    }

    public function getUserRoles($userId = NULL, $serviceId = NULL, $appId = NULL) {

        if ($appId == NULL)
            $appId = $this->appId;

        if ($serviceId == NULL)
            $serviceId = Blyn::app()->getActiveService()->getId();

        if ($userId == NULL)
            $userId = Blyn::app()->getCurrentUser()->getId();

        $id = 'userRole_' . $userId . "_" . $serviceId . "_" . $appId;

        if (isset($this->userRolesList[$id]) && $this->userRolesList[$id] != NULL)
            return $this->userRolesList[$id];

        $db = Blyn::app()->getAppDb();

        $roles = $db->getUserRoles($userId, $serviceId, $appId);

        $parentRoles = array();
        $childRoles = array();

        foreach ($roles as $role) {
            if ($role->isAdminType()) {
                $db->fetchChildAuthItems($parentRoles, $serviceId, $role->getId());
            }

            if ($role->isMemberType()) {
                $db->fetchParentAuthItems($childRoles, $serviceId, $role->getId());
            }

            if ($role->getAuthType() == BAuthItem::RoleType_Mixed) {
                $roles = $this->getRecursiveRoles($role, $serviceId);
                $adminRoles = $roles['adminRoles'];
                $memberRoles = $role['memberRoles'];

                foreach ($adminRoles as $role) {
                    $db->fetchChildAuthItems($parentRoles, $serviceId, $role->getId());
                }

                foreach ($memberRoles as $role) {
                    $db->fetchParentAuthItems($parentRoles, $serviceId, $role->getId());
                }
            }
        }

        $userRoles = $roles;

        if ($parentRoles != NULL) {
            foreach ($parentRoles as $key => $value) {
                $userRoles[$key] = $value;
            }
        }

        if ($childRoles != NULL) {
            foreach ($childRoles as $key => $value) {
                $userRoles[$key] = $value;
            }
        }

        //$userRoles = array_merge($roles, $parentRoles, $childRoles);
        //if any items exist, add server_member role into list
        if (count($userRoles) > 0) {
            $serviceEngineAppId = Blyn::app()->getActiveService()->getServiceEngine()->getId();
            $id = $this->getRoleId(BBasicRolesTemplate::SERVICE_MEMBER_ROLE, $serviceEngineAppId);

            if (!isset($userRoles[$id]))
                $userRoles[$id] = new BAuthItem($id);
        }

        $this->userRolesList[$id] = $userRoles;

        return $userRoles;
    }

    public function getUserCircles($serviceId, $userId) {
        return Blyn::app()->getAppDb()->getUserCircles($serviceId, $this->appId, $userId);
    }

    public function getCircleRoles($circleId, $serviceAppId, $serviceId = null) {
        return Blyn::app()->getAppDb()->getCircleRoles($circleId, $serviceId, $serviceAppId);
    }

    public function getUserOperations($user, $service, $roleType) {
        $operations = array();

        $roles = $this->getUserRoles($user, $service);

        $db = Blyn::app()->getAppDb();

        foreach ($roles as $role) {
            if ($role->getAuthType == $roleType) {
                array_merge($operations, $this->getRoleOperation($role));
            }

            $childRoles = $this->getChildRoles($service, $role);

            foreach ($childRoles as $role) {
                if ($role->getAuthType == $roleType) {
                    array_merge($operations, $this->getRoleOperation($service, $role));
                }
            }
        }

        return $operations;
    }

    //this function convert a string of roles to ids
    public function getRoleIdList($roleString) {

        if (is_array($roleString))
            $roleArray = $roleString;
        else {
            $roleArray = explode(',', $roleString);
        }

        /*

          //add service_member into roleIdList if any admin role exist
          if (!in_array('service_member', $roleArray) &&
          (in_array('service_admin', $roleArray) ||
          in_array('workteam_admin', $roleArray) ||
          in_array('servedteam_admin', $roleArray)
          )) {
          array_push($roleArray, 'service_member');
          }
         * 
         */

        $rolesId = array();

        foreach ($roleArray as $name => $role) {

            if (is_numeric($role)) {
                array_push($rolesId, $role);
                break;
            }

            if (is_array($role) && !is_numeric($name))
                $role = $name;

            if ($role == BBasicRolesTemplate::LOGIN_MEMBER_ROLE) {
                array_push($rolesId, BBasicRolesTemplate::LOGIN_MEMBER_ROLE_ID);
                break;
            }


            if (preg_match('/' . $role . '/', "service_admin,service_member,workteam_admin,workteam_member,servedteam_admin,servedteam_member")) {
                $roleName = $role;
                $appName = "BServiceEngine";
            } else {
                list($roleName, $appName) = explode("@", $role);
            }


            $appId = Blyn::app()->getAppDb()->getServiceAppId($appName);

            $roleId = $this->getRoleId($roleName, $appId);

            array_push($rolesId, $roleId);
        }

        return $rolesId;
    }

    public function addServiceChildRole($service, $parentRole, $childRole) {
        $db = Blyn::app()->getAppDb();

        $db->addServiceChildRole($service->getId(), $parentRole->getId(), $childRole->getId());
    }

    public function createRecursiveRole($role) {

        $roleType = $role['type'];
        $roleName = $role['name'];

        $roleShowName = $roleName;

        if (isset($role['showname']))
            $roleShowName = $role['showname'];

        $operations = array();

        if (isset($role['operations'])) {
            $ops = $role['operations'];
            foreach ($ops as $opName => $funcName) {
                $operation = new BMApplicationOperation();
                $operation->operationname = $opName;
                $operation->functionname = $funcName;
                $operation->save();
                array_push($operations, $operation);
            }
        }

        $newRole = $this->createRole($roleName, $roleType, $roleShowName);
        $this->addOperationToRole($newRole, $operations);

        foreach ($role as $name => $value) {
            if ($name != 'operations' && is_array($value)) {
                $childRole = $this->getRole($name);
                $this->addChildRole($newRole, $childRole);
            }
        }

        return $newRole;
    }

    /**
     * the format of role= array('rolename'=>array('rolename'=>...))
     * @param type $role 
     */
    public function createRecursiveRole2($role, $roleType = NULL) {
        //first add all roles into db
        $this->addRecursiveRoles($role, $roleType);
        //build parent-child relationship
        $this->addRecursiveChildRole($role);
    }

    public function addRecursiveRoles($role, $roleType = NULL) {

        foreach ($role as $key => $value) {
            $authType = isset($value['type']) ? $value['type'] : $roleType == NULL ? $key : $roleType;
            $roleShowName = isset($value['showname']) ? $value['showname'] : $roleType == NULL ? $key : $roleType;
            $this->addRole($key, $authType, $roleShowName);

            foreach ($value as $subKey => $subValue) {
                $authType = isset($subValue['type']) ? $subValue['type'] : $roleType == NULL ? $subKey : $roleType;
                $roleShowName = isset($subValue['showname']) ? $subValue['showname'] : $roleType == NULL ? $subKey : $roleType;
                $this->addRole($subKey, $authType, $roleShowName);
                if (is_array($subValue) && $subValue != NULL)
                    $this->addRecursiveRoles($subValue);
            }
        }
    }

    public function addRecursiveChildRole($role) {
        foreach ($role as $roleName => $value) {
            $parent = $roleName;
            foreach ($value as $subkey => $subvalue) {
                $child = $subkey;
                $this->addChildRole($parent, $child);
                if (is_array($subvalue) && $subvalue != NULL) {
                    $this->addRecursiveChildRole($value);
                }
            }
        }
    }

    private function getRoleOperation($role) {

        $db = Blyn::app()->getAppDb();

        return $db->getOperations($role);
    }

    public function getChildRoles($service, $role) {

        if (is_numeric($service))
            $serviceId = $service;
        if ($service instanceof BService)
            $serviceId = $service->getId();

        if (is_numeric($role))
            $roleId = $role;
        if ($role instanceof BAuthItem)
            $roleId = $role->getId();

        $childRoles = array();
        $db = Blyn::app()->getAppDb();
        $db->fetchChildAuthItems($childRoles, $service->getId(), $role->getId());
        return $childRoles;
    }

    public function getParentRoles($service, $role) {
        $parentRoles = array();
        $db = Blyn::app()->getAppDb();
        $db->fetchParentAuthItems($parentRoles, $service->getId(), $role->getId());
        return $parentRoles;
    }

    public function checkAccess($roles, $accessRoleIdList = array(), $denyRoleIdList = array()) {

        foreach ($roles as $role) {
            $roleId = $role->getId();
            if (in_array($roleId, $denyRoleIdList))
                break;
            if (in_array($roleId, $accessRoleIdList)) {
                return TRUE;
            }
        }

        return FALSE;
    }

    public function checkUserAccess($user, $accessRoleList = null, $denyRoleList = NULL) {

        $accessRoleIdList = $this->getRoleIdList($accessRoleList);
        $denyRoleIdList = $this->getRoleIdList($denyRoleList);

        //if accesslist have login member, return true
        if (in_array(BBasicRolesTemplate::LOGIN_MEMBER_ROLE_ID, $accessRoleIdList))
            return TRUE;

        $roleManager = Blyn::app()->getActiveServiceApp()->getAuthManager();

        if (!$user instanceof BUser)
            $user = Blyn::app()->getCurrentUser();

        $userRoles = $roleManager->getUserRoles($user->getId());

        return $this->checkAccess($userRoles, $accessRoleIdList, $denyRoleIdList);
    }

    //this function return all child roles that is not mixed role type
    //all child roles are classified into adminRoles and memberRoles
    /**
     * 
     * @param BAuthItem $role
     * @return type
     */
    public function getRecursiveRoles($role, $service, $adminRoles = array(), $memberRoles = array()) {

        if ($role->isAdminType()) {
            $roleId = $role->getId();
            $adminRoles[$roleId] = $role;
        }

        if ($role->isMemberType()) {
            $roleId = $role->getId();
            $memberRoles[$roleId] = $role;
        }

        if ($role->type == BAuthItem::RoleType_Mixed) {
            $childRoles = $this->getChildRoles($service, $role);

            foreach ($childRoles as $role) {
                $this->getRecursiveRoles($role, $service, $adminRoles, $memberRoles);
            }
        }

        $userRoles = array();

        foreach ($adminRoles as $role) {
            $childrenAdminRoles = $this->getChildRoles($service, $role);

            foreach ($childrenAdminRoles as $role) {
                $roleId = $role->getId();
                $userRoles[$roleId] = $role;
            }
        }

        foreach ($memberRoles as $role) {
            $parentMemberRoles = $this->getParentRoles($service, $role);

            foreach ($parentMemberRoles as $role) {
                $roleId = $role->getId();
                $userRoles[$roleId] = $role;
            }
        }

        return array('adminRoles' => $adminRoles, 'memberRoles' => $memberRoles, 'allRoles' => $userRoles);
    }

    public function isMemberOfCircle($serviceId, $userId, $circleId) {
        $circles = $this->getUserCircles($serviceId, $userId);

        if (in_array($circleId, array_keys($circles)))
            return TRUE;

        return FALSE;
    }

    public function getOperationPermits($operationName, $serviceId, $serviceAppId = NULL) {
        if ($serviceAppId == NULL) {
            $serviceAppId = $this->appId;
        }

        $app = BServiceApp::getInstanceById($serviceAppId);

        //first catch permits from serviceApp meta data
        $config = $app->getConfigManager()->getConfig('appOperations');
        $permits = isset($config[$operationName]['permit']) ? $config[$operationName]['permit'] : NULL;

        //means operation should be receiver
        
        if (isset($config[$operationName]['operationContract'])) {
            //$opContract = $config['operationContract'];
            if ($config[$operationName]['operationContract']['myRole'] !== 'sender') {
                //if not set receiverPermit, assign allowAccess to receiverPermit
                if (!isset($permits['receiverPermit']) && isset($permits['allowAccess']))
                    $permits['receiverPermit'] = $permits['allowAccess'];
            }
        }
        //second catch permits from data store that is input by user in runtime

        /*
          $permits = array();

          $db = Blyn::app()->getAppDb();

          $items = $db->getOperationPermits($serviceId, $serviceAppId, $operationId);

          foreach ($items as $id) {
          $permits[$id] = new BAuthItem($id);
          }
         * 
         */

        return $permits;
    }

}

?>
